package com.xci.sys.service;

import com.xci.core.annotation.Log;
import com.xci.core.annotation.Valid;
import com.xci.core.base.PrimaryKeyValue;
import com.xci.core.base.RestMessage;
import com.xci.core.domain.HistoryInfo;
import com.xci.core.exceptions.AppException;
import com.xci.core.helper.CacheHelper;
import com.xci.core.helper.Helper;
import com.xci.core.internal.*;
import com.xci.core.base.BaseService;
import com.xci.core.web.TreeNode;
import com.xci.sys.dao.DicCategoryDao;
import com.xci.sys.dao.DicDao;
import com.xci.sys.entity.SysDept;
import com.xci.sys.entity.SysDic;
import com.xci.sys.entity.SysDicCategory;
import com.xci.sys.filter.DicFilter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.cache.Cache;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 字典服务
 */
@Slf4j
@Service
public class DicService extends BaseService {
    @Resource
    private DicDao dicDao;//字典数据层对象
    @Resource
    private Cache dicCache;//字典缓存对象
    @Resource
    private DicCategoryDao dicCategoryDao;//字典类型数据层对象

    /**
     * 新建字典
     *
     * @param entity 字典对象
     */
    @Log(module = Const.Module.Dic, msg = "新建字典")
    @Transactional(rollbackFor = Exception.class)
    public RestMessage insert(@Valid SysDic entity) {
        return save(entity, true);
    }

    /**
     * 修改字典
     *
     * @param entity 字典对象
     */
    @Log(module = Const.Module.Dic, msg = "修改字典")
    @Transactional(rollbackFor = Exception.class)
    public RestMessage update(@Valid SysDic entity) {
        return save(entity, false);
    }

    /**
     * 修改字典状态
     *
     * @param ids    字典主键字符串,多个逗号隔开
     * @param status 字典状态
     */
    @Log(module = Const.Module.Dic, msg = "修改字典状态")
    @Transactional(rollbackFor = Exception.class)
    public RestMessage updateStatus(@NotBlank(message = "请指定字典主键") String ids,
                                    @NotNull(message = "请指定字典状态") Boolean status) {
        String[] idList = Helper.splitToArray(ids);
        for (String id : idList) {
            SysDic old = dicDao.selectById(id);
            if (old == null) throw new AppException("无效的字典主键:" + id);

            dicDao.updateStatus(id, status);
            dicCache.evict(old.getCode());
        }
        return RestMessage.success();
    }

    /**
     * 更新字典类型编码
     *
     * @param oldCode 原字典类型编码
     * @param newCode 新字典类型编码
     */
    @Log(module = Const.Module.Dic, msg = "更新字典类型编码")
    @Transactional(rollbackFor = Exception.class)
    public RestMessage updateCode(@NotBlank(message = "请指定原字典类型编码") String oldCode, @NotBlank(message = "请指定新字典类型编码") String newCode) {
        dicDao.updateCode(oldCode, newCode);
        dicCache.evict(oldCode);
        return RestMessage.success();
    }

    /**
     * 批量修改字段值
     *
     * @param keyValues 字段值集合
     */
    @Log(module = Const.Module.Dic, msg = "批量更新字段值")
    @Transactional(rollbackFor = Exception.class)
    public RestMessage batchUpdate(List<PrimaryKeyValue> keyValues) {
        return batchUpdateParent(keyValues, new BatchUpdateCallback() {
            @Override
            public void updateParentId(String id, String parentId) {
                dicDao.updateParentId(id, parentId);
            }

            @Override
            public void updatePath(String id, String path) {
                dicDao.updatePath(id, path);
            }

            @Override
            public void after() {
                var keyValue = keyValues.get(0);
                var dic = dicDao.selectById(keyValue.getId());
                if (dic != null) {
                    dicCache.evict(dic.getCode());
                }
            }
        });
    }

    /**
     * 批量保存字典对象
     *
     * @param entities 字典对象集合
     */
    @Log(module = Const.Module.Role, msg = "批量保存字典对象")
    @Transactional(rollbackFor = Exception.class)
    public RestMessage batchSave(@Valid List<SysDic> entities) {
        for (SysDic dic : entities) {
            if (Helper.isBlank(dic.getId())) {                   //无主键,则新增
                save(dic, true).ifFailException();
            } else if (!dicDao.existById(dic.getId())) {   //主键不存在,则新增
                save(dic, true).ifFailException();
            } else {                                      //否则修改
                save(dic, false).ifFailException();
            }
        }
        return RestMessage.success();
    }

    /**
     * 删除字典
     *
     * @param ids 字典主键字符串,多个逗号隔开
     */
    @Log(module = Const.Module.Dic, msg = "删除字典")
    @Transactional(rollbackFor = Exception.class)
    public RestMessage delete(@NotBlank(message = "请指定字典主键多个逗号隔开") String ids) {
        String[] idList = Helper.splitToArray(ids);
        for (String id : idList) {
            SysDic old = dicDao.selectById(id);
            if (old == null) throw new AppException("无效的字典主键:" + id);

            dicDao.delete(id);
            dicCache.evict(old.getCode());
            saveDeleteHistory(HistoryInfo.builder().pk(old.getId()).before(old).build());
        }
        return RestMessage.success();
    }

    /**
     * 根据字典类型编码删除
     *
     * @param code 字典类型编码
     * @return 返回影响的行数
     */
    @Log(module = Const.Module.Dic, msg = "根据字典类型编码删除")
    @Transactional(rollbackFor = Exception.class)
    public RestMessage deleteByCode(@NotBlank(message = "请指定字典类型编码") String code) {
        dicDao.deleteByCode(code);
        dicCache.evict(code);
        return RestMessage.success();
    }

    /**
     * 检查字典编码是否存在
     *
     * @param code 字典类型编码
     * @return 如果存在返回true, 否则返回false
     */
    public Boolean existByCode(@NotBlank(message = "请指定字典类型编码") String code) {
        return dicDao.existByCode(code);
    }

    /**
     * 查询单个字典对象
     *
     * @param id 字典主键
     * @return 返回字典对象
     */
    public SysDic selectById(@NotBlank(message = "请指定字典主键") String id) {
        return dicDao.selectById(id);
    }

    /**
     * 根据类型编码查询字典数量
     *
     * @param code 字典类型编码
     */
    public Long selectCountByCode(@NotBlank(message = "请指定字典类型编码") String code) {
        return dicDao.selectCountByCode(code);
    }

    /**
     * 查询字典列表
     *
     * @param code 字典类型编码
     * @return 返回字典列表
     */
    public List<SysDic> selectListByCode(@NotBlank(message = "请指定字典类型编码") String code) {
        return CacheHelper.getCacheValue(dicCache, code, () -> {
            DicFilter filter = new DicFilter();
            filter.setCode(code);
            filter.setStatus(true);
            return dicDao.selectList(filter);
        });
    }

    /**
     * 根据字典名称查询数对应值
     *
     * @param code        字典类型编码
     * @param name       字典名称
     * @param defaultValue 找不到指定的项值时返回的默认值
     * @return 返回数据字典键值对
     */
    public String selectValueByName(@NotBlank(message = "请指定字典类型编码") String code,
                                    @NotBlank(message = "请指定字典名称") String name, String defaultValue) {
        if (Helper.isBlank(code) || Helper.isBlank(name)) {
            return defaultValue;
        }
        List<SysDic> dicList = selectListByCode(code);
        for (SysDic dic : dicList) {
            if (dic.getName().equalsIgnoreCase(name)) {
                return dic.getValue();
            }
        }
        return defaultValue;
    }

    /**
     * 根据字典值查询对应名称
     *
     * @param code        字典类型编码
     * @param value       字典值
     * @param defaultName 找不到指定的项值时返回的默认名称
     * @return 返回数据字典键值对
     */
    public String selectNameByValue(@NotBlank(message = "请指定字典类型编码") String code,
                                    @NotBlank(message = "请指定字典值") String value, String defaultName) {
        if (Helper.isBlank(code) || Helper.isBlank(value)) {
            return defaultName;
        }
        List<SysDic> dicList = selectListByCode(code);
        for (SysDic dic : dicList) {
            if (dic.getValue().equalsIgnoreCase(value)) {
                return dic.getName();
            }
        }
        return defaultName;
    }

    /**
     * 查询字典列表
     *
     * @param filter 字典过滤条件
     * @return 返回字典列表
     */
    public List<SysDic> selectList(DicFilter filter) {
        return dicDao.selectList(filter);
    }

    /**
     * 刷新字典缓存
     */
    public void refreshCache() {
        dicCache.clear();
        List<SysDicCategory> categoryList = dicCategoryDao.selectList();
        DicFilter filter = new DicFilter();
        filter.setStatus(true);
        List<SysDic> dicList = dicDao.selectList(filter);
        for (SysDicCategory category : categoryList) {
            String code = category.getCode();
            List<SysDic> list = dicList.parallelStream().filter(p -> p.getCode().equals(code)).collect(Collectors.toList());
            dicCache.put(code, list);
        }
        log.info("刷新系统字典缓存");
    }

    /**
     * 保存字典
     *
     * @param entity  字典对象
     * @param created 是否新建
     */
    private RestMessage save(SysDic entity, boolean created) {
        //自动添加主键
        if (created && Helper.isBlank(entity.getId())) {
            entity.setId(Helper.nextIdStr());
        }
        //字典名称简拼
        if (Helper.isBlank(entity.getSpell())) {
            entity.setSpell(Helper.getSpell(entity.getName()));
        }

        //字典名称唯一性验证
        if (dicDao.existByName(entity.getCode(), entity.getName(), created ? null : entity.getId())) {
            String msg = Helper.format("字典名称[{}]已经存在", entity.getName());
            return RestMessage.fail(msg);
        }

        if (created) {// 新建
            if (entity.getPath() == null || entity.getPath() == 0) {
                entity.setPath(dicDao.selectCountByCode(entity.getCode()) + 1);
            }
            dicDao.insert(entity);
            dicCache.evict(entity.getCode());
            saveInsertHistory(HistoryInfo.builder().pk(entity.getId()).after(entity).build());
        } else {     //修改
            var old = dicDao.selectById(entity.getId());
            dicDao.update(entity);
            dicCache.evict(entity.getCode());
            saveUpdateHistory(HistoryInfo.builder().pk(entity.getId()).before(old).after(entity).build());
        }
        return RestMessage.success();
    }

    /**
     * 转为节点列表
     *
     * @param models 模型列表
     */
    public List<TreeNode> convertToNodeList(List<SysDic> models) {
        List<TreeNode> nodes = new ArrayList<>();
        if (ObjectUtils.isEmpty(models)) {
            return nodes;
        }
        for (var item : models) {
            boolean hasChild = models.stream().anyMatch(p -> p.getParentId().equals(item.getId()));
            TreeNode node = new TreeNode();
            node.setId(item.getId());
            node.setPid(item.getParentId());
            node.setText(item.getName());
            node.setSpell(item.getSpell());
            node.setLeaf(hasChild ? 0 : 1);
            nodes.add(node);
        }
        return nodes;
    }
}